//
//  People.m
//  DCTest
//
//  Created by Joinwe on 16/6/30.
//  Copyright © 2016年 Joinwe. All rights reserved.
//

#import "People.h"
#import <objc/runtime.h>
@interface People()

@property (strong) NSMutableDictionary *properties;

@end

/**
 * 消息传递机制的工作原理
 如果用实例对象调用实例方法，会到实例的isa指针指向的对象（也就是类对象）操作。
 如果调用的是类方法，就会到类对象的isa指针指向的对象（也就是元类对象）中操作。
 
 首先，在相应操作的对象中的缓存方法列表中找调用的方法，如果找到，转向相应实现并执行。
 如果没找到，在相应操作的对象中的方法列表中找调用的方法，如果找到，转向相应实现执行
 如果没找到，去父类指针所指向的对象中执行1，2.
 以此类推，如果一直到根类还没找到，转向拦截调用。
 如果没有重写拦截调用的方法，程序报错。
 
 * 在OC中调用方法最终会翻译成调用方法实现的函数指针，并传递给它一个对象指针、一个选择器和一组函数参数。每个OC消息表达式都会转化为对objc_msgSend或者是一个紧密相关函数的调用
 * 1.检查接收对象是否为 nil ，如果是，调用 nil 处理程序。
 * 2.在垃圾收集环境中(iOS并不支持)，检查有没有短路选择器(retain,release,autorelease,retainCount)，如果有，返回self，这意味着垃圾收集环境中retainCount会返回self。
 * 3.检查类缓存中是不是已经有方法实现了，有的话，直接调用。
 * 4.比较请求选择器和类中定义的选择器，如果找到了调用方法实现。
 * 5.比较请求的选择器和父类中定义的选择器，然后是父类的父类，以此类推。如果找到选择器，调用方法实现。
 * 6.调用 resolveInstanceMethod:(或 resolveClassMethod:)。如果它返回YES，那么重新开始。这一次对象会响应这个选择器，一般是因为它已经调用过 class_addMethod。
 * 7.调用 forwardingTargetForSelector:，如果返回非 nil ，那就把消息发送到返回的对象上。这里不需要返回 self，否则会造成死循环。
 * 8.调用 methodSignatureForSelector:，如果返回非 nil ，创建一个 NSInvocation 并传给 forwardInvocation:。
 * 9.调用 doesNotRecognizeSelector: ，默认的实现是抛出异常。
 *
 */
@implementation People
//当一个属性声明成@dynamic其实是在向编译器保证，尽管它现在找不到实现，但是在运行时会有可用的实现，这样编译器就不会去自动合成一个ivar了。
@dynamic givenName,surnName;

- (id)init{
    if (self = [super init]) {
        _properties = [[NSMutableDictionary alloc] init];
    }
    return self;
}


static id propertyIMP(id self, SEL _cmd){
    return [[self properties] valueForKey:NSStringFromSelector(_cmd)];
}

static void setPropertyIMP(id self, SEL _cmd, id aValue){
    id value = [aValue copy];
    NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy];
    
    //删除 “set”和“:”，并将第一个字母变为小写
    [key deleteCharactersInRange:NSMakeRange(0, 3)];
    [key deleteCharactersInRange:NSMakeRange([key length] - 1, 1)];
    
    
    NSString *firstChar = [key substringToIndex:1];
    
    [key replaceCharactersInRange:NSMakeRange(0, 1) withString:[firstChar lowercaseString]];
    
    [[self properties] setValue:value forKey:key];
}

+ (BOOL)resolveInstanceMethod:(SEL)aSEL{
    if ([NSStringFromSelector(aSEL) hasPrefix:@"set"]) {
        class_addMethod([self class], aSEL, (IMP)setPropertyIMP, "v@:@");
    }else{
        class_addMethod([self class], aSEL, (IMP)propertyIMP, "@@:");
    }
    return YES;
}


@end
